#include "sai.h"
#include "fsl_sai.h"
#include "fsl_sai_edma.h"
#include "fsl_dmamux.h"
#include "fsl_common.h"
#include "appCommon.h"

#include "fsl_iomuxc.h"
#include "fsl_gpio.h"


#define SAI_PERIPHERAL SAI1
#define SAI_CHANNEL (0)
#define SAI_IRQ SAI1_IRQn
#define SAI_TxIRQHandler SAI1_IRQHandler
#define SAI_TX_SYNC_MODE kSAI_ModeAsync
#define SAI_RX_SYNC_MODE kSAI_ModeSync
#define SAI_MCLK_OUTPUT true
#define SAI_MASTER_SLAVE kSAI_Master


#if CFG_I2S_WORDWIDTH == 16
#define SAI_AUDIO_BIT_WIDTH     kSAI_WordWidth16bits
#elif CFG_I2S_WORDWIDTH == 24
#define SAI_AUDIO_BIT_WIDTH     kSAI_WordWidth32bits
#elif CFG_I2S_WORDWIDTH == 32
#define SAI_AUDIO_BIT_WIDTH     kSAI_WordWidth32bits
#endif
#define SAI_AUDIO_SAMPLE_RATE   ((sai_sample_rate_t)CFG_I2S_SAMPLERATE)
#define SAI_AUDIO_DATA_CHANNEL  ((sai_word_width_t)CFG_I2S_CHANNEL)
#define SAI_AUDIO_MASTER_CLOCK  (CLOCK_GetFreq(kCLOCK_AudioPllClk) / (CLOCK_GetDiv(kCLOCK_Sai1Div) + 1U) / (CLOCK_GetDiv(kCLOCK_Sai1PreDiv) + 1U))

/* IRQ */
#define SAI_TX_IRQ SAI1_IRQn
#define SAI_RX_IRQ SAI1_IRQn

/* DMA */
#define DMA_PERIPHERAL DMA0
#define DMAMUX_PERIPHERAL DMAMUX
#define EDMA_TX_CHANNEL (0U)
#define EDMA_RX_CHANNEL (1U)
#define SAI_TX_SOURCE kDmaRequestMuxSai1Tx
#define SAI_RX_SOURCE kDmaRequestMuxSai1Rx

AT_NONCACHEABLE_SECTION_INIT(sai_edma_handle_t txHandle);
AT_NONCACHEABLE_SECTION_INIT(sai_edma_handle_t rxHandle);
edma_handle_t dmaTxHandle = {0}, dmaRxHandle = {0};

static void rx_callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);
static void tx_callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData);

void saiInit(void)
{
    edma_config_t dmaConfig = {0};
    sai_transceiver_t config;
    
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_12_SAI1_RX_DATA00,0U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_13_SAI1_TX_DATA00,0U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_14_SAI1_TX_BCLK,0U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_15_SAI1_TX_SYNC,0U);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_03_SAI1_MCLK,0U);

#if (SAI_MASTER_SLAVE == kSAI_Master)
    IOMUXC_GPR->GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK;
#endif
    /* Init DMAMUX */
    DMAMUX_Init(DMAMUX_PERIPHERAL);
    DMAMUX_SetSource(DMAMUX_PERIPHERAL, EDMA_TX_CHANNEL, (uint8_t)SAI_TX_SOURCE);
    DMAMUX_EnableChannel(DMAMUX_PERIPHERAL, EDMA_TX_CHANNEL);
    DMAMUX_SetSource(DMAMUX_PERIPHERAL, EDMA_RX_CHANNEL, (uint8_t)SAI_RX_SOURCE);
    DMAMUX_EnableChannel(DMAMUX_PERIPHERAL, EDMA_RX_CHANNEL);

    /* Init DMA and create handle for DMA */
    EDMA_GetDefaultConfig(&dmaConfig);
    EDMA_Init(DMA_PERIPHERAL, &dmaConfig);
    EDMA_CreateHandle(&dmaTxHandle, DMA_PERIPHERAL, EDMA_TX_CHANNEL);
    EDMA_CreateHandle(&dmaRxHandle, DMA_PERIPHERAL, EDMA_RX_CHANNEL);
#if defined(FSL_FEATURE_EDMA_HAS_CHANNEL_MUX) && FSL_FEATURE_EDMA_HAS_CHANNEL_MUX
    EDMA_SetChannelMux(DMA_PERIPHERAL, EDMA_TX_CHANNEL, SAI_TX_EDMA_CHANNEL);
    EDMA_SetChannelMux(DMA_PERIPHERAL, EDMA_RX_CHANNEL, SAI_RX_EDMA_CHANNEL);
#endif

    /* SAI init */
    SAI_Init(SAI_PERIPHERAL);

    SAI_TransferTxCreateHandleEDMA(SAI_PERIPHERAL, &txHandle, tx_callback, NULL, &dmaTxHandle);
    SAI_TransferRxCreateHandleEDMA(SAI_PERIPHERAL, &rxHandle, rx_callback, NULL, &dmaRxHandle);

    /* I2S mode configurations */
    SAI_GetClassicI2SConfig(&config, SAI_AUDIO_BIT_WIDTH, kSAI_Stereo, 1U << SAI_CHANNEL);
    config.syncMode = SAI_TX_SYNC_MODE;
    config.masterSlave = SAI_MASTER_SLAVE;
    SAI_TransferTxSetConfigEDMA(SAI_PERIPHERAL, &txHandle, &config);
    config.syncMode = SAI_RX_SYNC_MODE;
    SAI_TransferRxSetConfigEDMA(SAI_PERIPHERAL, &rxHandle, &config);

    /* set bit clock divider */
    SAI_TxSetBitClockRate(SAI_PERIPHERAL, SAI_AUDIO_MASTER_CLOCK, SAI_AUDIO_SAMPLE_RATE, SAI_AUDIO_BIT_WIDTH,
                          SAI_AUDIO_DATA_CHANNEL);
    SAI_RxSetBitClockRate(SAI_PERIPHERAL, SAI_AUDIO_MASTER_CLOCK, SAI_AUDIO_SAMPLE_RATE, SAI_AUDIO_BIT_WIDTH,
                          SAI_AUDIO_DATA_CHANNEL);

    NVIC_SetPriority(DMA1_DMA17_IRQn, 3);
    NVIC_SetPriority(DMA0_DMA16_IRQn, 3);
}

static void rx_callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
{
    sai_transfer_t transfer;
    if (kStatus_SAI_RxError == status)
    {
        /* Handle the error. */
    }
    else
    {
        saiReceiveCallback();
    }
}

static void tx_callback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
{
    sai_transfer_t transfer;
    
    if (kStatus_SAI_TxError == status)
    {
        /* Handle the error. */
    }
    else
    {
        saiSendCallback();
    }
}

__WEAK void saiReceiveCallback(void)
{
}

__WEAK void saiSendCallback(void)
{
}

void saiStartSend(uint8_t *buffer, size_t byteSize)
{
    sai_transfer_t transfer;

    transfer.data = buffer;
    transfer.dataSize = byteSize;
    SAI_TransferSendEDMA(SAI_PERIPHERAL,&txHandle,&transfer);
}

void saiStartReceive(uint8_t *buffer, size_t byteSize)
{
    sai_transfer_t transfer;

    transfer.data = buffer;
    transfer.dataSize = byteSize;
    SAI_TransferReceiveEDMA(SAI_PERIPHERAL,&rxHandle,&transfer);
}

